package java_thinking.unit_17;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @Desc:   容器理论
 *          1.享元设计模式：可以在普通的解决方案需要过多的对象或者产生普通对象太占用空间的时候使用享元
 *          享元是的对象的一部分可以被具体化，因此，与对象中的所有事物都包含在对象内部不同的是我们可以
 *          在更加高效的外部表中查找对象的一部分或者整体(或者通过某些其他节省空间的计算来产生对象的一
 *          部分或整体)
 *
 * @author: cww
 * @DateTime: 2019-04-10 22:22
 */

public class ContainerTheory {
}
/**
 * HashMap: 是一个采用哈希表实现的键值对集合，继承自 AbstractMap，实现了 Map 接口
 *          https://blog.csdn.net/u011240877/article/details/53351188
 *  1.Map存储结构：
 *      1.KeySet:是Map中键(key)的集合，以Set形式保存，不允许重复，可以用keySet()方法获得；
 *      2.values:是Map中值(values)的集合，以Collection的形式保存，可以重复，可以用Map.value()方法获得；
 *      3.Entry：是Map接口中静态内部接口，表示一个键值对的映射，可以通过Map.entrySet()方法获得一组Entry集合，保存在Set中
 *  2.Map三种遍历方式：
 *      1.使用 keySet 遍历
 *      2.使用 values 遍历
 *      3.使用 Entry 遍历
 *  3.Map插入顺序：见流程图
 *      1.扩容：1.1新初始化哈希表时，容量为默认容量，阈值为 容量*加载因子
 *              1.2已有哈希表扩容时，容量、阈值均翻倍
 *              1.3如果之前这个桶的节点类型是树，需要把新哈希表里当前桶也变成树形结构
 *              1.4复制给新哈希表中需要重新索引（rehash），这里采用的计算方法是 e.hash & (newCap - 1)，等价于 e.hash % newCap
 *              结合扩容源码可以发现扩容的确开销很大，需要迭代所有的元素，rehash、赋值，还得保留原来的数据结构
 *      2.查找：2.1先计算哈希值;
 *              2.2然后再用 (n - 1) & hash 计算出桶的位置
 *              2.3在桶里的链表进行遍历查找+
 *  4.缺点：不同步，也可以考虑用 Collections.synchronizedMap 包一层，变成个线程安全的 Map
 *          Map m = Collections.synchronizedMap(new HashMap(...))
 *
 *  5.为什么哈希表的容量一定要是 2的整数次幂?
 *      5.1使用减法替代取模，提升计算效率；
 *      5.2为了使不同 hash 值发生碰撞的概率更小，尽可能促使元素在哈希表中均匀地散列。
 *
 *  6.HashMap 中 equals() 和 hashCode() 有什么作用？
 *      6.1HashMap 的添加、获取时需要通过 key 的 hashCode() 进行 hash()，然后计算下标 (n-1 & hash)，从而获得要找的同的位置
 *      6.2当发生冲突（碰撞）时，利用 key.equals() 方法去链表或树中去查找对应的节点
 *
 *  7.你知道 hash 的实现吗？为什么要这样实现？
 *      7.1 是通过hashCode() 的高16位异或低16位实现的：(h = k.hashCode()) ^ (h >>> 16)
 *      主要是从速度、功效、质量 来考虑的，这么做可以在桶的 n 比较小的时候，保证高低 bit 都参与到 hash 的计算中，
 *      同时位运算不会有太大的开销，因为散列码不比是独一无二的，我们更应该关注生成速度，而不是唯一性，但是通过
 *      hashCode()和equals(),必须能够完全确定对象的身份。
 *
 *  8.HashMap 在 JDK 1.8 后新增的红黑树结构：https://blog.csdn.net/u011240877/article/details/53358305
 *                              红黑树理解;https://blog.csdn.net/u011240877/article/details/53329023 还没看！
 */

/**
 *
 * Set：
 *      HashSet: 无序，但查询快，使用散列函数存储,存入HashSet的元素必须定义hashCode()，原因是用来寻址
 *      TreeSet: 按比较结果顺序存储，元素存储在红黑树结构中，元素必须实现Comparable接口；
 *      LinkedHashSet: 按照添加的顺序存储，保留了HashSet的查询速度，使用散列函数存储，使用了链表来维护元素的插入顺序
 *                      所以在用地带器遍历Set时，结果会按元素插入的次序显示，元素也必须定义hashCode()方法
    Set<Map.Entry<K, V>> entrySet();
        1.这是Map接口的一个entrySet()方法；
        2.这个方法返回一个Set(集合)接口的一个实现；
        3.这个Set的类型为Map.Entry接口的一个实现类；
        3.Map.Entry接口实现主要是能够获取集合的键值对
        4.public Set<K> keySet()返回此映射中所包含的键的 set 视图
        5.map的keySet()方法只返回一个set实例，所以当从key1中删除一个对象时候，其他也将会受到影响。
        6.针对于 set不能进行 add操作 ，否则会报java.lang.UnsupportedOperationException；原因：
    public Set<K> keySet()返回此映射中所包含的键的 set 视图。该集合受映射的支持，所以映射的变化也反映在该集合中，反之亦然。
        该集合支持元素的移除，通过Iterator.remove、Set.remove、removeAll、retainAll和 clear 操作，从该映射中移除相应的映射关系。
        它不支持 add 或 addAll 操作。
        7.HashMap的entrySet()方法返回一个特殊的Set，这个Set使用EntryIterator遍历，而这个
        Iterator则直接操作于HashMap的内部存储结构table上。通过这种方式实现了“视图”的功能。
        整个过程不需要任何辅助存储空间。
    p.s.从这一点也可以看出为什么entrySet()是遍历HashMap最高效的方法，原因很简单，因为这种方式和HashMap内部的存储方式是一致的。
 8.遍历entrySet()流程：
    1.map.entrySet()返回Set<Map.Entry<K,V>> entrySet对象集合
    2.该entrySet对象实现了iterator()方法，返回Iterator<Map.Entry<K,V>>对象
    3.iterator()中实现了EntryIterator()对象的返回；
    4.EntryIterator()对象继承自HashIterator，HashIterator实现了对底层Node<K,V> table[]数组的读取

 */

class Evolution {

    static String[][] s = {{"KA","VA"},{"KB","VB"},{"KC","VC"}};
    public static class MapEntry<K,V> implements Map.Entry<K,V> {
        /**
         * MapEntry是用来获取集合指定键值的，所以要告诉MapEntry你要取集合哪个位置的内容
         */
        private int index;

        public MapEntry(int index) {
            this.index = index;
        }

        @Override
        public K getKey() {
            return (K)s[index][0];
        }

        @Override
        public V getValue() {
            return (V)s[index][1];
        }

        @Override
        public V setValue(V value) {
            s[index][1] = (String) value;
            return null;
        }
    }

    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("KA","VA");
        map.put("KB","VB");
        map.put("KC","VC");

        Set<Map.Entry<String,String>> set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        for (Map.Entry entry:map.entrySet()) {
            System.out.println(entry.getValue()+" , "+entry.getValue());
        }

//        System.out.println("mapEntry.getKey() : " + mapEntry.getKey());;
//        System.out.println("mapEntry.getValue() : " + mapEntry.getValue());
//        System.out.println("mapEntry.setValue() : " + mapEntry.setValue("VVB"));
//        System.out.println("mapEntry.getValue() : " + mapEntry.getValue());

    }

}

class Test {
    public static void main(String[] args) {
        System.out.println(1 << 4);
    }
}